package com.rzt.cft.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.rzt.cft.constant.Constants;
import com.rzt.cft.constant.ServiceStatus;
import com.rzt.cft.exception.CustomException;
import com.rzt.cft.dto.sys.SysUserDto;
import com.rzt.cft.entity.TokenVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * <pre>
 * jwt工具类
 * </pre>
 *
 * 2018年4月28日
 */
@Slf4j
@Component
public class JwtUtil {

    // 秘钥
    private static String SECRET;

    @Value("${jwt.secret}")
    public void setSECRET(String secret) {
        this.SECRET = secret;
    }

    public static String userPrimaryKey = "userId";

    public static TokenVO createToken(SysUserDto user) {
        TokenVO vo = new TokenVO();
        String token = JwtUtil.sign(user.getId(), user.getUserPwd(), Constants.EXPIRE_TIME);
        String refreshToken = JwtUtil.sign(user.getId() + "", user.getUserPwd(), Constants.REFRESH_TOKEN_EXPIRE, true);
        vo.setToken(token);
        vo.setRefleshToken(refreshToken);
        vo.setTokenExpire(Constants.EXPIRE_TIME);
        vo.setRefreshTokenExpire(Constants.REFRESH_TOKEN_EXPIRE);
        return vo;
    }

    public static String createToken(String userId){
        return JwtUtil.sign(userId, SECRET, Constants.REFRESH_TOKEN_EXPIRE);
    }

    /**
     * 获得token中的信息无需secret解密也能获得
     *
     * @return token中包含的签发时间
     */
    public static Date getIssuedAt(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getIssuedAt();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * token是否过期
     *
     * @return true：过期
     */
    public static boolean isTokenExpired(String token) {
        Date now = Calendar.getInstance().getTime();
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getExpiresAt().before(now);
    }

    /**
     * <pre>
     * </pre>
     *
     * 2018年4月28日
     *
     * @param token  即jwt
     * @param userId 用户id
     * @param secret 用户的secret
     */
    public static void verify(String token, String userId, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm).withClaim(userPrimaryKey, userId).build();
            verifier.verify(token);
        } catch (TokenExpiredException exception) {
            log.info("token 签名校验失败,过期：{}", token);
            throw new ExpiredCredentialsException(ServiceStatus.AUTHORIZATION_EXPIRED.getMessage());
        } catch (InvalidClaimException exception2) {
            log.info("token 签名校验失败,数据异常：{}", token);
            throw new AuthenticationException(ServiceStatus.AUTHORIZATION_INVALID.getMessage());
        } catch (Exception exception3) {
            log.info("token 签名校验失败：{}", token);
            throw new CustomException(ServiceStatus.AUTHORIZATION_INVALID);
        }
    }

    public static void verify(String token, String userId, String secret, boolean isRefreshToken) {
        if (isRefreshToken) {
            secret += "_REFRESH_TOKEN";
        }
        verify(token, userId, secret);
    }

    /**
     * <pre>
     * 获得token中的信息无需secret解密也能获得
     * </pre>
     *
     * 2018年4月28日
     *
     * @param token token
     */
    public static Integer getUserId(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Claim claim = jwt.getClaim(userPrimaryKey);
            return claim.asInt();
        } catch (JWTDecodeException e) {
            log.warn("token解码获取{}失败：{}", userPrimaryKey, token);
            return null;
        }
    }

    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *
     * @param userId 用户标识
     * @param secret 加密密钥
     * @param expire 有效期，毫秒值
     */
    public static String sign(String userId, String secret, long expire) {
        try {
            Date date = new Date(System.currentTimeMillis() + expire);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            return JWT.create().withClaim(userPrimaryKey, userId).withExpiresAt(date).withIssuedAt(new Date()).sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new CustomException(ServiceStatus.AUTHORIZATION_SIGN_FAILED);
        }
    }

    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *
     * @param userId 用户标识
     * @param secret 加密密钥
     * @param expire 有效期，毫秒值
     */
    public static String sign(String userId, String secret, long expire, boolean isRefreshToken) {
        if (isRefreshToken) {
            secret += "_REFRESH_TOKEN";
        }
        return sign(userId, secret, expire);
    }

    public static String verifyTokenAndGetUserId(String token) {

        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            Map<String, Claim> claims = jwt.getClaims();
            Claim claim = claims.get("userId");
            return claim.asString();
        } catch (Exception exception){
//			exception.printStackTrace();
        }
        return "";
    }
}
